
#include <stdint.h>
#include "Bit.h"


uint8_t BitBuffer_read(uint8_t BitBuf[], uint32_t BitNum){
	uint32_t ByteOffset = getByteOffset(BitNum);
	uint32_t BitOffset = getBitOffset(BitNum);

	return Bit_get(BitBuf[ByteOffset], BitOffset);
}

void BitBuffer_write(uint8_t BitBuf[], uint32_t BitNum, uint8_t val){
	uint32_t ByteOffset = getByteOffset(BitNum);
	uint32_t BitOffset = getBitOffset(BitNum);

	if(val != 0){
		Bit_set(BitBuf[ByteOffset], BitOffset);
	}
	else{
		Bit_clear(BitBuf[ByteOffset], BitOffset);
	}
}

#define DIR_UP		1
#define DIR_DOWN	(-1)

static void BitBuffer_copyWithDirect(uint8_t From[], uint32_t FromOffset, uint8_t To[], uint32_t ToOffset, 
									 uint32_t Len, int32_t Dir){
	 uint32_t i;
	 uint32_t bit;
	 int32_t from_position = FromOffset;

	 for(i = 0; i < Len; i++){
		 bit = BitBuffer_read(From, from_position);
		 BitBuffer_write(To, ToOffset + i, bit);
		 from_position += Dir;
	 }
}

void BitBuffer_copy(uint8_t From[], uint32_t FromOffset, uint8_t To[], uint32_t ToOffset, uint32_t Len){
	BitBuffer_copyWithDirect(From, FromOffset, To, ToOffset, Len, DIR_UP);
}

static int32_t BitBuffer_compareWithDirection(uint8_t BitBuf[], uint32_t Offset, 
											  uint8_t Compare[], uint32_t Len, int32_t Dir){
	uint32_t i;
	uint32_t bit1, bit2;
	int32_t result = 0;
	int32_t pos = Offset;

	for(i = 0; i < Len; i++){
		bit1 = BitBuffer_read(BitBuf, pos);
		bit2 = BitBuffer_read(Compare, i);
		result = bit1 - bit2;
		if(result != 0){
		  break;
		}
		pos += Dir;
	}
	return result;	
}

int32_t BitBuffer_compare(uint8_t BitBuf[], uint32_t Offset, uint8_t Compare[], uint32_t Len){
	return BitBuffer_compareWithDirection(BitBuf, Offset, Compare, Len, DIR_UP);
}

int32_t BitBuffer_compareBackward(uint8_t BitBuf[], uint32_t Offset, uint8_t Compare[], uint32_t Len){
	if((Offset + 1) < Len){
		return -1;
	}
	return BitBuffer_compareWithDirection(BitBuf, Offset, Compare, Len, DIR_DOWN);
}

uint8_t BitBuffer_countSetBitsInByte(uint8_t Byte){
	static const uint8_t SetBitsInNibble[16] =
		/*0, 1,  2,  3,   4,  5,  6,   7,  8,  9,   A,  B,  C,  D,  E,   F*/
	{0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};

	return SetBitsInNibble[Byte & 0x0F] + SetBitsInNibble[Byte >> 4];
}

uint32_t BitBuffer_countSetBits(uint8_t BitBuf[], uint32_t Len){
	uint32_t sum = 0;
	uint32_t i;

	for(i = 0; i < Len; i++){
		sum += BitBuffer_countSetBitsInByte(BitBuf[i]);
	}

	return sum;
}

uint32_t BitArray_getSize(bit_array_t *Array){
	return Array->Size;
}

uint32_t BitArray_getLength(bit_array_t *Array){
	return Array->Len;
}

void BitArray_initialize(bit_array_t *Array, uint8_t Buf[], uint32_t Size)
{
	Array->Buffer = Buf;
	Array->Len = 0;
	Array->Size = Size * 8;
}

static uint32_t BitArray_checkInvalidReadOffset(bit_array_t *Array, uint32_t Offset){
	return Array->Len < Offset;
}

static uint32_t BitArray_checkOverRead(bit_array_t *Array, uint32_t Offset, uint32_t Len){
	return  Array->Len < (Offset + Len);
}

uint32_t BitArray_read(bit_array_t *Array, uint8_t ReadBuf[], uint32_t Offset, uint32_t Len){
	
	if(BitArray_checkInvalidReadOffset(Array, Offset)){
		return 1;
	}

	if(BitArray_checkOverRead(Array, Offset, Len)){
		return 1;
	}

	BitBuffer_copy(Array->Buffer, Offset, ReadBuf, 0, Len);

	return 0;
}

static uint32_t BitArray_checkOverWrite(bit_array_t *Array, uint32_t Len){
	return Array->Len + Len > Array->Size;
}

uint32_t BitArray_write(bit_array_t *Array, uint8_t WriteBuf[], uint32_t WriteBufOffset, uint32_t Len){
	if(BitArray_checkOverWrite(Array, Len)){
		return 1;
	}

	BitBuffer_copy(WriteBuf, WriteBufOffset, Array->Buffer, Array->Len, Len);
	Array->Len += Len;
	return 0;
}

int32_t BitArray_searchForward(bit_array_t *Array, uint32_t Offset, uint8_t BitBuf[], uint32_t Len){
	uint32_t i;
	uint8_t *buf = Array->Buffer;
	int32_t result;
	uint32_t l;
	uint32_t index = Offset;

	if(Offset > BitArray_getLength(Array)){
		return -1;
	}

	if(Len > BitArray_getLength(Array)){
		return -1;
	}

	if(Len + Offset > BitArray_getLength(Array)){
		return -1;
	}

	l = BitArray_getLength(Array) - (Len + index);
	for(i = 0; i < l; i++){
		result = BitBuffer_compare(buf, index, BitBuf, Len);
		if(result == 0){
			return index;
		}
		index++;
	}
	return -1;
}

int32_t BitArray_searchBackward(bit_array_t *Array, uint32_t Offset, uint8_t BitBuf[], uint32_t Len){
	uint32_t i;
	uint8_t *buf = Array->Buffer;
	int32_t result;
	uint32_t l;
	uint32_t index = Offset;

	if(Offset > BitArray_getLength(Array)){
		return -1;
	}

	if(Len > BitArray_getLength(Array)){
		return -1;
	}

	if(Len > index){
		return -1;
	}

	l = index - Len;
	for(i = 0; i < l; i++){
		result = BitBuffer_compareBackward(buf, index, BitBuf, Len);
		if(result == 0){
			return index;
		}
		index--;
	}
	return -1;
}

/************************************************************************/
/* 
	Bit Visitor
*/
/************************************************************************/

uint32_t BitVisitor_getOffset(bit_visitor_t *Visitor){
	return Visitor->Offset;
}

void BitVisitor_setOffset(bit_visitor_t *Visitor, uint32_t Offset){
	Visitor->Offset = Offset < BitArray_getLength(Visitor->Array) ? Offset : BitArray_getLength(Visitor->Array);
}

void BitVisitor_initialize(bit_visitor_t *Visitor, bit_array_t *Array){
	Visitor->Array = Array;
	BitVisitor_setOffset(Visitor, 0);
}

int32_t BitVisitor_walkForwardNext(bit_visitor_t *Visitor, uint8_t Window[], uint32_t Size){
	uint32_t ret;

	ret = BitArray_read(Visitor->Array, Window, Visitor->Offset, Size);
	if(ret){
		return -1;
	}
	Visitor->Offset += Size;
	return Visitor->Offset;
}

static void BitArray_readBackward(bit_array_t *Array, uint32_t Offset, uint8_t Window[], uint32_t Size){
	uint8_t *buf = Array->Buffer;
	uint32_t len = BitArray_getLength(Array);
	uint32_t from_offset = len - Offset - 1;

	BitBuffer_copyWithDirect(buf, from_offset, Window, 0, Size, DIR_DOWN);
}

static uint32_t BitVisitor_checkOverWalk(bit_visitor_t *Visitor, uint32_t Size){
	return (Visitor->Offset + Size) > BitArray_getLength(Visitor->Array);
}

int32_t BitVisitor_walkBackwardNext(bit_visitor_t *Visitor, uint8_t Window[], uint32_t Size){
	
	if(BitVisitor_checkOverWalk(Visitor,Size)){
		return -1;
	}

	BitArray_readBackward(Visitor->Array, Visitor->Offset, Window, Size);
	Visitor->Offset += Size;
	return Visitor->Offset;
}

int32_t BitVisitor_searchForwardNext(bit_visitor_t *Visitor, uint8_t BitBuf[], uint32_t Len){
	int32_t offset;

	offset = BitArray_searchForward(Visitor->Array, Visitor->Offset, BitBuf, Len);
	if(offset >= 0){
		Visitor->Offset += offset + 1;
	}
	return offset;
}

